#include "ui.hpp"
#include <algorithm>

using namespace Shiny;
using namespace Shiny::Widgets;

Ui::type2builder Ui::_type2builders[] = {
  { "Ui",          &Ui::BuildUi          },
  { "Text",        &Ui::BuildTextWidget  },
  { "ProgressBar", &Ui::BuildProgressBar },
  { "List",        &Ui::BuildList        },
  { "",     0                            }
};

void Ui::GenerateFromFile(Data data, Container* father)
{
  Data         type     = data["type"];
  Container*   container = 0;
  sf::Vector2f position;
  Data         size, children, positionX, positionY, isVisible, isMovable, bgColor;
  AlignType    toSetAlign  = None;
  VAlignType   toSetVAlign = VNone;

  for (unsigned short i = 0 ; _type2builders[i].function != 0 ; ++i)
  {
    if (_type2builders[i].key != type.Value())
      continue ;
    container = (this->*_type2builders[i].function)(data);
    break ;
  }
  if (!container) container = new Container();

  // SET GRAPHICS
  Data                          bgImg        = data["background-img"];
  Data                          bgAnim       = data["background-anim"];
  SpriteAnimate::AnimationArray animArray;

  if      (!(bgImg.Nil()))
    SpriteAnimate::LoadTexture(animArray, bgImg.Value(), *_textureManager);
  else if (!(bgAnim.Nil()))
    SpriteAnimate::LoadAnimation(animArray, bgAnim.Value(), *_animationManager);
  if (animArray.size() > 0)
  {
    Data                        bgHoverImg   = data["background-hover-img"];
    Data                        bgHoverAnim  = data["background-hover-anim"];
    Data                        bgActiveImg  = data["background-active-img"];
    Data                        bgActiveAnim = data["background-active-anim"];

    container->_hasHoverGraphics  = true;
    container->_hasActiveGraphics = true;
    if      (!(bgHoverImg.Nil()))
      SpriteAnimate::LoadTexture(animArray, bgHoverImg.Value(), *_textureManager);
    else if (!(bgHoverAnim.Nil()))
      SpriteAnimate::LoadAnimation(animArray, bgHoverAnim.Value(), *_animationManager);
    else
      container->_hasHoverGraphics = false;
    if      (!(bgActiveImg.Nil()))
      SpriteAnimate::LoadTexture(animArray, bgActiveImg.Value(), *_textureManager);
    else if (!(bgActiveAnim.Nil()))
      SpriteAnimate::LoadAnimation(animArray, bgActiveAnim.Value(), *_animationManager);
    else
      container->_hasActiveGraphics = false;
    container->BackgroundSprite().SetAnimations(animArray);
    container->BackgroundSprite().SetAnimation(0);
    container->SetSize(sf::Vector2f(container->BackgroundSprite().GetSize()));
  }
  // END SET GRAPHICS

  if (father)     { father->AddWidget(container); }
  
  if ((!(positionX = data["x"]).Nil()) && (!(positionY = data["y"]).Nil()))
    container->SetPosition(sf::Vector2f(positionX, positionY));
  if (!((isVisible = data["visible"]).Nil()))
    container->ForceVisibility(isVisible.Value() == "1");
  if (!((isMovable = data["movable"])).Nil())
    container->SetMovable(isMovable.Value() == "1");
  if (!((bgColor   = data["background-color"]).Nil()))
    container->SetBackgroundColor(GetColor(bgColor));
  sf::Vector2f paddingLeftTop, paddingRightBot;
  unsigned char mouseAwarenessFlag;
  
  GenerateMouseAwareness(data["mouse-awareness"], mouseAwarenessFlag);
  GenerateAnimation(data, container);
  GeneratePadding(data["padding-left"],  data["padding-top"], paddingLeftTop,  true);
  GeneratePadding(data["padding-right"], data["padding-bot"], paddingRightBot, false);
  GenerateAlign(data["align"], data["valign"], toSetAlign, toSetVAlign);
  container->SetPadding(paddingLeftTop.x, paddingLeftTop.y, paddingRightBot.x, paddingRightBot.y);
  container->SetName(data.Key());
  container->SetAlign(toSetAlign, toSetVAlign);
  container->SetMouseAwareness(mouseAwarenessFlag);
  if (!(children = data["widgets"]).Nil())
  {
    Data::iterator it  = children.begin();
    Data::iterator end = children.end();

    for (; it != end ; ++it)
      GenerateFromFile((*it), container);
  }
}

void       Ui::GenerateAnimation(Data data, Container* container)
{
  Data     animationType  = data["animation-type"];
  Data     animationSpeed = data["animation-speed"];

  if (!(animationType.Nil()))
  {
    Container::TransitionMethod method = 0;

    if      (animationType == "Fade")
      method = &Container::AnimationFade;
    else if (animationType == "Scale")
      method = &Container::AnimationZoom;
    container->SetAnimationType(method);
  }
  if (!(animationSpeed.Nil()))
    container->SetAnimationSpeed(animationSpeed);
}

void       Ui::GenerateMouseAwareness(Data mouseAwareness, unsigned char& flag)
{
  flag = MouseHover | MouseClick | MouseButton | MouseWheel2;
  if (!(mouseAwareness.Nil()))
  {
    if (mouseAwareness.Value() == "None")
      flag = MouseNone;
    else
    {
      if (mouseAwareness["hover"] == "0")
	flag -= MouseHover;
      if (mouseAwareness["click"] == "0")
	flag -= MouseClick;
      if (mouseAwareness["button"] == "0")
	flag -= MouseButton;
      if (mouseAwareness["wheel"] == "0")
	flag -= MouseWheel2;
    }
  }
}

void       Ui::GeneratePadding(Data h, Data v, sf::Vector2f& vector, bool positive)
{
  if (!(h.Nil()))
    vector.x = (float)(h) * (positive ? 1 : -1);
  if (!(v.Nil()))
    vector.y = (float)(v) * (positive ? 1 : -1);
}

void       Ui::GenerateAlign(Data halign, Data valign, AlignType& toSetAlign, VAlignType& toSetVAlign)
{
  if (!(halign.Nil()))
  {
    if      (halign.Value() == "Center")
      toSetAlign = Center;
    else if (halign.Value() == "Left")
      toSetAlign = Left;
    else if (halign.Value() == "Right")
      toSetAlign = Right;
  }
  if (!(valign.Nil()))
  {
    if      (valign.Value() == "Center")
      toSetVAlign = VCenter;
    else if (valign.Value() == "Top")
      toSetVAlign = Top;
    else if (valign.Value() == "Bottom")
      toSetVAlign = Bot;
  }
}

Container* Ui::BuildList(Data data)
{
  WidgetList* widget       = new WidgetList();
  bool        verticalList = false;
  bool        leftToRight  = true;
  Data        vertical, fieldLeftToRight, fieldWheelStep, fieldSlideEnabled;
  
  if (!(vertical = data["verticalList"]).Nil())
    verticalList = vertical.Value() == "1";
  if (!(fieldLeftToRight = data["leftToRight"]).Nil())
    leftToRight  = fieldLeftToRight.Value() == "1";
  if (!(fieldWheelStep = data["wheelStep"]).Nil())
    widget->SetWheelStep(fieldWheelStep);
  if (!(fieldSlideEnabled = data["slideEnabled"]).Nil())
    widget->SetSlideEnabled(fieldSlideEnabled.Value() == "1");
  widget->SetVerticalList(verticalList);
  widget->SetLeftToRight(leftToRight);
  return (widget);
}

Container* Ui::BuildProgressBar(Data data)
{
  ProgressBar* widget     = new ProgressBar();
  bool         isSide     = false;
  bool         isVertical = false;
  Data         texture, percentage, vertical, side, color;

  if (!(texture    = data["progressBar"]).Nil())
    widget->SetProgressBarTexture((*_textureManager)[texture.Value()]);
  if (!(vertical   = data["way"]).Nil())
    isVertical    = vertical.Value() == "vertical";
  if (!(side       = data["side"]).Nil())
    isSide        = side.Value()     == "inversed";
  widget->SetSide(isVertical, isSide);
  if (!(color      = data["color"]).Nil())
    widget->SetColor(GetColor(color));
  if (!(percentage = data["percentage"]).Nil())
    widget->SetPercentage(percentage);
  return (widget);
}

Container* Ui::BuildUi(Data data)
{
  Data           uiFilter   = data["ui_filter"];
  Data           textures   = data["textures"];
  Data           animations = data["animations"];
  TextureList*   texList;
  AnimationList* animList;
  Ui*            widget;

  if (textures.Nil())
    texList = _textureManager;
  else
  {
    DataTree* texFile = DataTree::Factory::Yaml(textures.Value());
    
    if (texFile)
    {
      texList = new TextureList(texFile);
      delete texFile;
    }
  }
  
  if (animations.Nil())
    animList = _animationManager;
  else
  {
    DataTree* animFile = DataTree::Factory::Yaml(animations.Value());
    
    if (animFile)
    {
      animList = new AnimationList(animFile, *texList);
      delete animFile;
    }
  }
  
  widget = new Ui(texList, animList);
  widget->_ownTextureList   = !textures.Nil()   && texList;
  widget->_ownAnimationList = !animations.Nil() && animList;
  
  if (!(uiFilter.Nil()))
    widget->_backgroundColor = GetColor(uiFilter);
  return (widget);
}

Container* Ui::BuildTextWidget(Data data)
{
  TextWidget*  widget = new TextWidget();
  Data         text, font, padding;

  text    = data["value"];
  font    = data["font"];
  padding = data["padding"];
  widget->SetText(text.Value());
  return (widget);
}

sf::Color Ui::GetColor(Data data)
{
  Data       fr, fg, fb, fa;
  float      r, g, b, a;

  fr = data["red"];
  fg = data["green"];
  fb = data["blue"];
  fa = data["alpha"];
  r  = ((fr.Nil()) ? 0   : (int)fr);
  g  = ((fg.Nil()) ? 0   : (int)fg);
  b  = ((fb.Nil()) ? 0   : (int)fb);
  a  = ((fa.Nil()) ? 255 : (int)fa);
  return (sf::Color(r, g, b, a));
}

Ui::Ui(Data data, TextureList* tm, AnimationList* am) : _textureManager(tm), _animationManager(am)
{
  Data::iterator current = data["widgets"].begin();
  Data::iterator end     = data["widgets"].end();

  _ownTextureList   = false;
  _ownAnimationList = false;
  _typeCode         = type2code<Ui>::code;
  _name             = data.Value();
  if (!data["textures"].Nil())
  {
    DataTree* textures = DataTree::Factory::Yaml(data["textures"].Value());
    
    if (textures)
    {
      _textureManager = new TextureList(textures);
      _ownTextureList = true;
      delete textures;
    }
  }
  if (!data["animations"].Nil())
  {
    DataTree* animations = DataTree::Factory::Yaml(data["animations"].Value());
    
    if (animations)
    {
      _animationManager = new AnimationList(animations, *_textureManager);
      _ownAnimationList = true;
      delete animations;
    }
  }
  while (current != end)
  {
    GenerateFromFile(*current, this);
    ++current;
  }
}

Ui::~Ui()
{
  if (_ownTextureList)   delete _textureManager;
  if (_ownAnimationList) delete _animationManager;
}

void         Ui::Render(sf::RenderTarget& target)
{
  if (target.GetHeight() != GetHeight() || target.GetWidth() != GetWidth())
    SetSize(sf::Vector2f(target.GetWidth(), target.GetHeight()));
  Container::Render(target);
}
